package cn.zxf.note.note.note;

import cn.hutool.core.util.StrUtil;
import cn.zxf.note.common.exception.BizException;
import cn.zxf.note.note.note.dto.NoteItemDto;
import cn.zxf.note.note.note.dto.NoteSingleLineDto;
import cn.zxf.note.note.parse.*;
import cn.zxf.note.note.review.NoteReviewSupport;
import cn.zxf.note.note.type.NoteType;
import cn.zxf.note.note.type.NoteTypeSupport;
import cn.zxf.note.security.UserAuthService;
import com.github.zengxf.common.domain.Page;
import com.github.zengxf.common.domain.PageResult;
import com.github.zengxf.common.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import jakarta.transaction.Transactional;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Created by zengxf on 2019/4/2.
 */
@Slf4j
@Component
public class NoteService {

    @Autowired
    private NoteRepository repos;
    @Autowired
    private NoteDao dao;
    @Autowired
    private NoteSupport support;
    @Autowired
    private UserAuthService authService;
    @Autowired
    private NoteTypeSupport typeSupport;
    @Autowired
    private NoteReviewSupport reviewSupport;

    @Transactional
    public NoteItemDto save(Integer userId, NoteItemDto dto) {
        Note note = Optional.ofNullable(dto.getId())
                .flatMap(repos::findById)
                .orElseGet(Note::new);
        boolean insert = note.getId() == null;
        {
            authService.validate(userId, note.getUserId());
            NoteType type = typeSupport.validateAndGet(dto.getTypeId());
            authService.validate(userId, type.getUserId());
        }
        BeanUtils.copyProperties(dto, note);
        note.setUserId(userId);
        if (insert)
            note.setStatus(NoteStatusEnum.NORMAL.code);
        repos.save(note);
        if (insert)
            reviewSupport.batchInsert(userId, note.getId());
        return BeanUtils.transform(note, NoteItemDto::new);
    }

    public NoteItemDto findOne(Integer userId, Integer noteId) {
        Note note = support.validateAndGet(noteId);
        authService.validate(userId, note.getUserId());
        NoteItemDto dto = BeanUtils.transform(note, NoteItemDto::new);
        NoteType type = typeSupport.validateAndGet(note.getTypeId());
        dto.setTypeName(type.getName());
        return dto;
    }

    @Transactional
    public NoteItemDto delete(Integer userId, Integer noteId) {
        Note note = support.validateAndGet(noteId);
        authService.validate(userId, note.getUserId());
        note.setStatus(NoteStatusEnum.DELETED.code);
        repos.save(note);
        reviewSupport.deleteByNoteId(noteId);
        return BeanUtils.transform(note, NoteItemDto::new);
    }

    @Transactional
    public void importNotes(Integer userId, String content) {
        if (StrUtil.isEmpty(content))
            throw BizException.of("要导入的内容为空");
        authService.validate(userId);

        try {
            MonthNoteVo vo = ParseMonthNote.ofContent(content)
                    .parseContent();
            String month = vo.getMonth();
            vo.getDayNotes().forEach(dayVo -> {
                dayVo.getNotes().stream()
                        .collect(Collectors.groupingBy(NoteVo::getType, LinkedHashMap::new, Collectors.toList()))
                        .forEach((type, notes) -> {
                            notes.forEach(note -> {
                                log.info("导入 note: {}", note);
                                support.saveForImport(userId, month, dayVo.getDay(), note);
                            });
                        });
            });
        } catch (IOException e) {
            log.error("导入出错", e);
            throw BizException.of("导入出错");
        } catch (ParseNoteException e) {
            log.error("导入出错", e);
            throw BizException.of("导入出错，格式不对");
        }
    }

    @Transactional
    public void importNotesForDay(Integer userId, String content) {
        if (content == null || content.isBlank())
            throw BizException.of("类型为空");
        authService.validate(userId);

        try {
            DayNoteVo vo = ParseDayNote.ofContent(content)
                    .parseContent();
            vo.getNotes().stream()
                    .collect(Collectors.groupingBy(NoteVo::getType, LinkedHashMap::new, Collectors.toList()))
                    .forEach((type, notes) -> {
                        notes.forEach(note -> {
                            log.info("导入 note: {}", note);
                            support.saveForImport(userId, vo.getDay(), note);
                        });
                    });
        } catch (IOException e) {
            log.error("导入出错", e);
            throw BizException.of("导入出错");
        } catch (ParseNoteException e) {
            log.error("导入出错", e);
            throw BizException.of("导入出错，格式不对");
        }
    }

    public void transferNotes(Integer userId, Integer fromTypeId, Integer toTypeId) {
        NoteType from = typeSupport.validateAndGet(fromTypeId);
        NoteType to = typeSupport.validateAndGet(toTypeId);
        authService.validate(userId, from.getUserId());
        authService.validate(userId, to.getUserId());
        dao.transfer(userId, fromTypeId, toTypeId);
    }

    public PageResult<NoteSingleLineDto> findByType(Integer userId, Integer typeId, Integer pageIndex, Integer pageSize) {
        NoteType type = typeSupport.validateAndGet(typeId);
        authService.validate(userId, type.getUserId());
        Page<NoteSingleLineDto> page = dao.pageByType(userId, typeId, pageIndex, pageSize);
        return PageResult.of(page.list, page.total, pageIndex, pageSize);
    }

    public PageResult<NoteSingleLineDto> search(
            Integer userId, Integer type, String keyword, Integer pageIndex, Integer pageSize
    ) {
        authService.validate(userId);
        Page<NoteSingleLineDto> page = dao.pageSearch(userId, type, keyword, pageIndex, pageSize);
        return PageResult.of(page.list, page.total, pageIndex, pageSize);
    }

}
